package utils

import (
	"strings"

	"github.com/mojocn/base64Captcha"
)

// CaptchaConfig 验证码设置结构
type CaptchaConfig struct {
	CaptchaType   string
	DriverAudio   *base64Captcha.DriverAudio
	DriverString  *base64Captcha.DriverString
	DriverChinese *base64Captcha.DriverChinese
	DriverMath    *base64Captcha.DriverMath
	DriverDigit   *base64Captcha.DriverDigit
}

type CaptchaManager struct {
	conf    *CaptchaConfig
	captcha *base64Captcha.Captcha
	store   base64Captcha.Store //base64Captcha.DefaultMemStore

	maxFailCount int

	clientCaptchaStatus *ListItem
}

type IpStatus struct {
	failCount int
	ip        string
	id        string
}

func NewCaptchaManager(maxCount int, conf *CaptchaConfig) *CaptchaManager {
	m := &CaptchaManager{
		conf:         conf,
		store:        base64Captcha.DefaultMemStore,
		maxFailCount: maxCount,
	}
	m.initCaptcha()
	return m
}

func (c *CaptchaManager) initCaptcha() {
	var driver base64Captcha.Driver
	conf := c.conf
	//create base64 encoding captcha
	switch conf.CaptchaType {
	case "audio":
		driver = conf.DriverAudio
	case "string":
		driver = conf.DriverString.ConvertFonts()
	case "math":
		driver = conf.DriverMath.ConvertFonts()
	case "chinese":
		driver = conf.DriverChinese.ConvertFonts()
	default:
		driver = conf.DriverDigit
	}
	c.captcha = base64Captcha.NewCaptcha(driver, c.store)
}

func (c *CaptchaManager) GetStatus(ip string) *IpStatus {

	if c.clientCaptchaStatus != nil {
		itm, _ := c.clientCaptchaStatus.Find(func(data interface{}) bool {
			eq := data.(*IpStatus).ip == ip
			return eq
		})
		if itm != nil {
			return itm.Data.(*IpStatus)
		}
	}

	status := &IpStatus{
		ip: ip,
	}

	itm := NewListItem(status)
	if nil == c.clientCaptchaStatus {
		c.clientCaptchaStatus = itm
	} else {
		c.clientCaptchaStatus = c.clientCaptchaStatus.Push(itm)
	}

	return status
}

func (c *CaptchaManager) NeedCaptcha(ip string) bool {
	status := c.GetStatus(ip)
	return status.failCount >= c.maxFailCount
}

func (c *CaptchaManager) AddFailCount(ip string) {
	status := c.GetStatus(ip)
	status.failCount += 1
}

func (c *CaptchaManager) ClearCaptchaFailStatus(ip string) {
	status := c.GetStatus(ip)
	status.failCount = 0
	c.ClearCaptcha(ip)
}

func (c *CaptchaManager) GenerateCaptcha(ip string) (b64s, btype string, err error) {
	mtype := "img"
	id, b64, err := c.captcha.Generate()
	if err != nil {
		return b64, mtype, err
	}
	if c.conf.CaptchaType == "audio" {
		mtype = "audio"
	}
	status := c.GetStatus(ip)
	status.id = id
	return b64, mtype, err
}

func (c *CaptchaManager) Verify(ip, answer string, clear bool) bool {
	status := c.GetStatus(ip)
	answer = strings.ToLower(answer)
	id := status.id
	if id != "" {
		Debug("verify id %s , answer %s,real answer : %s", id, answer, c.store.Get(id, false))
		return c.store.Verify(id, answer, clear)
	}
	return false
}

func (c *CaptchaManager) ClearCaptcha(ip string) {
	status := c.GetStatus(ip)
	if status.id != "" {
		c.store.Verify(status.id, "", true)
	}
}
